iT邦幫忙

2024 iThome 鐵人賽

DAY 3
0
JavaScript

可愛又迷人的 Web API系列 第 3

Day3. 用 Drag and Drop API 拖曳網頁元素

  • 分享至 

  • xImage
  •  

我曾經在公司後台開發一個儀表板功能,使用者可以拖拉想要的圖表元件,並將元件移動、放大以排版。這個功能有用到 HTML5 的 Drag and Drop API,我們可以使用該 API,輕鬆完成拖曳畫面的元素來重新排序,也能將文件拖至特定區域進行上傳,非常的方便。

今天就跟大家分享這個好玩又有趣的 Drag and Drop API!

使用 Drag and Drop API 的優點

Drag and Drop API 可以讓我們定義哪些元素能被拖動,以及我們可在何處放置這些元素。此外還有一些事件,可以監聽控制拖放的過程。

他的優點包括但不限於:

  • 原生支援:這是瀏覽器內建的 Web API,不需要額外安裝 JavasScript 套件
  • 效能佳:承上,因為是瀏覽器原生的,所以效能會比安裝套件來得好
  • 跨應用程式的兼容性:可以在網頁與桌面環境無縫拖放

讓元素動起來,設定可拖動的元素

首先,我們先設定一個可以拖動的元素吧!

1. 設定 HTML 屬性

將元素的 draggable 設為 true

<div id="draggable-item" draggable="true">可以拖動的元素</diV>

2. 監聽 dragstart 事件

當使用者開始拖動元素後,會觸發 dragstart 事件

const draggableItem = document.getElementById('draggable-item');

draggableItem.addEventListener('dragstart', (event) => {
  console.log('e', event)
});

我們就能在網站上按住元素進行拖曳,也能在 console 面板看到相關的資訊

https://mukiwu.github.io/web-api-demo/img/3-1.gif

我們可以使用 dataTransfer.setData() 方法儲存當前被拖動元素的 ID,這個資料可以在後續放置元素時使用。

const draggableItem = document.getElementById('draggable-item');

draggableItem.addEventListener('dragstart', (event) => {
  event.dataTransfer.setData('text/plain', event.target.id);
});

3. 定義放置區域

先建立一個放置區域:

<div id="drop-zone" style="height: 300px; width: 300px; border: 1px solid black">放置區域</div>

然後在 drop 事件中,取得前面儲存的 event.target.id,藉由這個 id 找到對應的元素,再放到放置區域裡

const dropZone = document.getElementById('drop-zone');

dropZone.addEventListener('dragover', (event) => {
  event.preventDefault(); // 阻止默認行為以允許放置
});

dropZone.addEventListener('drop', (event) => {
  event.preventDefault();
  const draggedItemId = event.dataTransfer.getData('text/plain');
  const draggedItem = document.getElementById(draggedItemId);
  event.target.appendChild(draggedItem);
});

https://mukiwu.github.io/web-api-demo/img/3-2.gif

拖放事件

Drag and Drop API 提供了許多事件,讓我們能夠精確控制拖放的每個過程

事件名稱 觸發時機 觸發的物件
dragstart 使用者開始拖曳元素時 被拖曳的元素
drag 元素被拖曳時,會持續觸發 被拖曳的元素
dragenter 被拖曳的元素進入潛在的放置區域時 放置區域
dragover 被拖曳的元素在潛在的放置區域內移動時,會持續觸發 放置區域
dragleave 被拖曳的元素離開潛在的放置區域時 放置區域
drop 被拖曳的元素在有效的放置區域內被釋放時(例如:放開滑鼠左鍵) 放置區域
dragend 拖曳操作結束時(無論是否成功放置) 被拖曳的元素

註:

  1. 潛在的放置區域指的是任何可能接受拖放的元素,不僅限於最終允許放置的區域。
  2. 要使一個元素成為有效的放置區域,需要阻止 dragover 事件的默認行為。
  3. drop 事件只會在有效的放置區域內觸發,即那些在 dragover 事件中調用了 event.preventDefault() 的元素。

事件的運用範例

讓我們來利用這些事件寫兩個放置區域,元素可以在這兩個區域間自由移動,並選擇要放在哪個區域

<div id="draggable-item" draggable="true">可以拖動的元素</div>
<div class="flex">
  <div id="drop-zone-1" class="drop-zone">放置區域 1</div>
  <div id="drop-zone-2" class="drop-zone">放置區域 2</div>
</div>

  1. 先宣告變數
const draggableItem = document.getElementById('draggable-item');
const dropZones = document.querySelectorAll('.drop-zone');
const dropZone1 = document.getElementById('drop-zone-1');
const dropZone2 = document.getElementById('drop-zone-2');
  1. 使用 dragstart 以及 dragend 讓元素在拖曳狀態下變成半透明,表示這是當前選取的元素
draggableItem.addEventListener('dragstart', (event) => {
  event.dataTransfer.setData('text/plain', event.target.id);
  event.target.style.opacity = '0.5'; // 半透明效果
});

draggableItem.addEventListener('dragend', (event) => {
  event.target.style.opacity = ''; // 恢復正常外觀
});
  1. 在進入有效的放置區域(dragover),以及觸發釋放(drop)事件時,兩個放置區域會做一樣的事情,可用 forEach 迴圈處理。
dropZones.forEach(zone => {
  zone.addEventListener('dragover', (event) => {
	event.preventDefault(); // 阻止默認行為以允許放置
  });

  zone.addEventListener('drop', (event) => {
	event.preventDefault();
	const draggedItemId = event.dataTransfer.getData('text/plain');
	const draggedItem = document.getElementById(draggedItemId);
	event.target.appendChild(draggedItem);
	event.target.classList.remove('drag-over-yellow', 'drag-over-blue');
  });
});
  1. 當元素進入放置區域時,會偵測進入哪一塊放置區域做對應的樣式,這段也可以寫在 forEach 迴圈內,但為了方便介紹與好理解,我就不寫在迴圈內了。

    這邊針對 dropZone1 以及 dropZone2 做了 dragenter 以及 dragleave 兩個事件,將元素移入 / 移出時,會用不同的樣式表示元素進入了該放置區域。

dropZone1.addEventListener('dragenter', (event) => {
	event.target.classList.add('drag-over-yellow');
});
dropZone1.addEventListener('dragleave', (event) => {
	event.target.classList.remove('drag-over-yellow');
});

dropZone2.addEventListener('dragenter', (event) => {
	event.target.classList.add('drag-over-blue');
});
dropZone2.addEventListener('dragleave', (event) => {
	event.target.classList.remove('drag-over-blue');
});
  1. 所有用到的 CSS:
.drag-over-yellow {
  background-color: yellow;
}

.drag-over-blue {
  background-color: teal;
}

.drop-zone {
  height: 300px;
  width: 300px;
  border: 1px solid black;
  margin: 10px;
  padding: 10px;
}

#draggable-item {
  cursor: move;
  padding: 10px;
  background-color: #f0f0f0;
  display: inline-block;
}

.flex {
  display: flex;
}

呈現結果如下:

https://mukiwu.github.io/web-api-demo/img/3-3.gif

範例程式碼

線上範例網址:https://mukiwu.github.io/web-api-demo/drag-and-drop.html

小結

HTML5 的 Drag and Drop API 提供了強大而靈活的工具,我們透過設定可拖動元素、定義拖放區域、處理各種拖放事件,以及自定義視覺效果等技巧,可以大大提升網頁的使用者體驗。文章內容如有任何問題都歡迎留言討論唷!


上一篇
Day2. 用 Geolocation API 實作車子導航追蹤
下一篇
Day4. 認識 History API:SPA 的關鍵技術之一
系列文
可愛又迷人的 Web API31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言